package vip.liux.contracts.security.store.extensions;

import org.springframework.data.repository.ListCrudRepository;
import vip.liux.contracts.models.identity.vo.UserLoginInfo;
import vip.liux.contracts.security.core.extensions.*;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 表示用户持久化存储的抽象基类。
 *
 * @param <TUser>      表示用户的类型
 * @param <TKey>       用户主键的类型
 * @param <TUserLogin> 表示用户外部登录的类型
 * @param <TUserToken> 表示用户令牌的类型
 */
public abstract class UserStoreBase<TUser, TKey, TUserLogin, TUserToken>
        implements IUserLoginStore<TUser>, IUserPasswordStore<TUser>,
        IUserEmailStore<TUser>, IUserLockoutStore<TUser>,
        IUserPhoneNumberStore<TUser>, IQueryableUserStore<TUser, TKey>, IUserTwoFactorStore<TUser>,
        IUserAuthenticationTokenStore<TUser>, IUserAuthenticatorKeyStore<TUser>,
        IUserTwoFactorRecoveryCodeStore<TUser>, AutoCloseable {

    private static final String INTERNAL_LOGIN_PROVIDER = "[AspNetUserStore]";
    private static final String AUTHENTICATOR_KEY_TOKEN_NAME = "AuthenticatorKey";
    private static final String RECOVERY_CODE_TOKEN_NAME = "RecoveryCodes";
    /**
     * 错误描述器，用于描述存储操作中的错误。
     */
    protected IdentityErrorDescriber errorDescriber;
    private boolean disposed;

    /**
     * 创建新实例。
     *
     * @param describer 用于描述存储错误的错误描述器
     */
    public UserStoreBase(IdentityErrorDescriber describer) {
        if (describer == null) {
            throw new IllegalArgumentException("describer 不能为 null");
        }
        this.errorDescriber = describer;
    }

    /**
     * 为指定用户创建一个新的用户登录信息实例。
     *
     * @param user  关联的用户
     * @param login 关联的登录信息
     * @return 创建的用户登录信息
     */
    protected TUserLogin createUserLogin(TUser user, UserLoginInfo login) {
        throw new UnsupportedOperationException("需要在子类中实现");
    }

    /**
     * 为指定用户创建一个新的用户令牌实例。
     *
     * @param user          关联的用户
     * @param loginProvider 关联的登录提供程序
     * @param name          令牌名称
     * @param value         令牌值
     * @return 创建的用户令牌
     */
    protected TUserToken createUserToken(TUser user, String loginProvider, String name, String value) {
        throw new UnsupportedOperationException("需要在子类中实现");
    }

    /**
     * 获取指定用户的唯一标识符。
     *
     * @param user 需要获取标识符的用户
     * @return 指定用户的唯一标识符 
     */
    public String getUserId(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return convertIdToString(getUserIdInternal(user));
    }

    /**
     * 获取用户内部ID。
     * 子类应该实现此方法以返回用户的ID。
     *
     * @param user 用户
     * @return 用户ID
     */
    protected abstract TKey getUserIdInternal(TUser user);

    /**
     * 获取指定用户的用户名。
     *
     * @param user 需要获取用户名的用户
     * @return 指定用户的用户名 
     */
    public String getUserName(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getUserNameInternal(user);
    }

    /**
     * 获取用户名的内部实现。
     * 子类应该实现此方法以返回用户的用户名。
     *
     * @param user 用户
     * @return 用户名
     */
    protected abstract String getUserNameInternal(TUser user);

    /**
     * 为指定用户设置用户名。
     *
     * @param user     需要设置用户名的用户
     * @param userName 要设置的用户名 
     */
    public void setUserName(TUser user, String userName) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setUserNameInternal(user, userName);
    }

    /**
     * 设置用户名的内部实现。
     * 子类应该实现此方法以设置用户的用户名。
     *
     * @param user     用户
     * @param userName 用户名
     */
    protected abstract void setUserNameInternal(TUser user, String userName);

    /**
     * 获取指定用户的标准化用户名。
     *
     * @param user 需要获取标准化用户名的用户
     * @return 指定用户的标准化用户名 
     */
    public String getNormalizedUserName(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getNormalizedUserNameInternal(user);
    }

    /**
     * 获取标准化用户名的内部实现。
     * 子类应该实现此方法以返回用户的标准化用户名。
     *
     * @param user 用户
     * @return 标准化用户名
     */
    protected abstract String getNormalizedUserNameInternal(TUser user);

    /**
     * 为指定用户设置标准化用户名。
     *
     * @param user           需要设置标准化用户名的用户
     * @param normalizedName 要设置的标准化用户名 
     */
    public void setNormalizedUserName(TUser user, String normalizedName) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setNormalizedUserNameInternal(user, normalizedName);
    }

    /**
     * 设置标准化用户名的内部实现。
     * 子类应该实现此方法以设置用户的标准化用户名。
     *
     * @param user           用户
     * @param normalizedName 标准化用户名
     */
    protected abstract void setNormalizedUserNameInternal(TUser user, String normalizedName);

    /**
     * 在用户存储中创建指定的用户。
     *
     * @param user 要创建的用户
     * @return 创建操作的结果 
     */
    public abstract IdentityResult create(TUser user);

    /**
     * 在用户存储中更新指定的用户。
     *
     * @param user 要更新的用户
     * @return 更新操作的结果 
     */
    public abstract IdentityResult update(TUser user);

    /**
     * 从用户存储中删除指定的用户。
     *
     * @param user 要删除的用户
     * @return 删除操作的结果 
     */
    public abstract IdentityResult deleteUser(TUser user);

    /**
     * 根据用户ID查找并返回用户。
     *
     * @param userId 要搜索的用户ID
     * @return 包含匹配指定用户ID的用户的Optional对象，如果不存在则为空 
     */
    public abstract Optional<TUser> findById(String userId);

    /**
     * 将提供的ID转换为强类型的键对象。
     *
     * @param id 要转换的ID
     * @return 表示提供的ID的TKey实例
     */
    @SuppressWarnings("unchecked")
    public TKey convertIdFromString(String id) {
        if (id == null) {
            return null;
        }
        try {
            // 注意：实际实现需要根据TKey类型进行适当的转换
            return (TKey) id;
        } catch (Exception e) {
            throw new ClassCastException("无法将id转换为对象");
        }
    }

    /**
     * 将提供的ID转换为其字符串表示形式。
     *
     * @param id 要转换的ID
     * @return 提供的ID的字符串表示形式
     */
    public String convertIdToString(TKey id) {
        if (id == null) {
            return null;
        }
        return id.toString();
    }

    /**
     * 根据标准化用户名查找并返回用户。
     *
     * @param normalizedUserName 要搜索的标准化用户名
     * @return 包含匹配指定标准化用户名的用户的Optional对象，如果不存在则为空 
     */
    public abstract Optional<TUser> findByName(String normalizedUserName);

    /**
     * 获取存储中的所有用户。
     *
     * @return 用户集合 
     */
    public abstract ListCrudRepository<TUser, TKey> getUserRepository();

    /**
     * 设置用户的密码哈希。
     *
     * @param user         需要设置密码哈希的用户
     * @param passwordHash 要设置的密码哈希 
     */
    public void setPasswordHash(TUser user, String passwordHash) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setPasswordHashInternal(user, passwordHash);
    }

    /**
     * 设置密码哈希的内部实现。
     * 子类应该实现此方法以设置用户的密码哈希。
     *
     * @param user         用户
     * @param passwordHash 密码哈希
     */
    protected abstract void setPasswordHashInternal(TUser user, String passwordHash);

    /**
     * 获取用户的密码哈希。
     *
     * @param user 需要获取密码哈希的用户
     * @return 用户的密码哈希 
     */
    public String getPasswordHash(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getPasswordHashInternal(user);
    }

    /**
     * 获取密码哈希的内部实现。
     * 子类应该实现此方法以返回用户的密码哈希。
     *
     * @param user 用户
     * @return 密码哈希
     */
    protected abstract String getPasswordHashInternal(TUser user);

    /**
     * 检查用户是否有密码。
     *
     * @param user 需要检查的用户
     * @return 如果用户有密码则为true，否则为false 
     */
    public boolean hasPassword(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getPasswordHashInternal(user) != null;
    }

    /**
     * 根据用户ID查找用户。
     *
     * @param userId 用户ID
     * @return 用户（如果存在） 
     */
    protected abstract Optional<TUser> findUser(TKey userId);

    /**
     * 根据用户ID、登录提供程序和提供程序键查找用户登录信息。
     *
     * @param userId        用户ID
     * @param loginProvider 登录提供程序
     * @param providerKey   提供程序键
     * @return 用户登录信息（如果存在） 
     */
    protected abstract Optional<TUserLogin> findUserLogin(TKey userId, String loginProvider, String providerKey);

    /**
     * 根据登录提供程序和提供程序键查找用户登录信息。
     *
     * @param loginProvider 登录提供程序
     * @param providerKey   提供程序键
     * @return 用户登录信息（如果存在） 
     */
    protected abstract Optional<TUserLogin> findUserLogin(String loginProvider, String providerKey);

    /**
     * 检查对象是否已释放，如果已释放则抛出异常。
     *
     * @throws IllegalStateException 如果对象已被释放
     */
    protected void throwIfDisposed() {
        if (disposed) {
            throw new IllegalStateException(getClass().getName() + " 已被释放");
        }
    }

    /**
     * 释放存储使用的所有资源。
     * 实现应确保在此方法被调用后不能再使用存储。
     *
     * @throws Exception 如果关闭过程中发生错误
     */
    @Override
    public void close() throws Exception {
        disposed = true;
    }

    /**
     * 为指定用户添加外部登录信息。
     *
     * @param user  需要添加登录信息的用户
     * @param login 要添加的登录信息 
     */
    public abstract void addLogin(TUser user, UserLoginInfo login);

    /**
     * 从指定用户移除外部登录提供程序。
     *
     * @param user          需要移除登录信息的用户
     * @param loginProvider 要移除的登录提供程序
     * @param providerKey   登录提供程序用于标识用户的键 
     */
    public abstract void removeLogin(TUser user, String loginProvider, String providerKey);

    /**
     * 获取指定用户关联的所有外部登录信息。
     *
     * @param user 需要获取登录信息的用户
     * @return 包含指定用户的所有外部登录信息的列表 
     */
    public abstract List<UserLoginInfo> getLogins(TUser user);

    /**
     * 根据登录提供程序和提供程序键查找用户。
     *
     * @param loginProvider 提供用户身份标识的登录提供程序
     * @param providerKey   登录提供程序用于标识用户的键
     * @return 包含匹配指定登录信息的用户的Optional对象，如果不存在则为空 
     */
    public Optional<TUser> findByLogin(String loginProvider, String providerKey) {
        throwIfDisposed();

        Optional<TUserLogin> userLogin = findUserLogin(loginProvider, providerKey);
        if (userLogin.isPresent()) {
            TKey userId = getUserIdFromLogin(userLogin.get());
            return findUser(userId);
        }
        return Optional.empty();
    }

    /**
     * 获取登录中的用户ID。
     *
     * @param userLogin 用户登录信息
     * @return 用户ID
     */
    protected abstract TKey getUserIdFromLogin(TUserLogin userLogin);

    /**
     * 获取用户的电子邮件确认状态。
     *
     * @param user 需要检查的用户
     * @return 如果用户电子邮件已确认则为true，否则为false 
     */
    public boolean getEmailConfirmed(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getEmailConfirmedInternal(user);
    }

    /**
     * 获取用户电子邮件确认状态的内部实现。
     *
     * @param user 用户
     * @return 电子邮件确认状态
     */
    protected abstract boolean getEmailConfirmedInternal(TUser user);

    /**
     * 设置用户的电子邮件确认状态。
     *
     * @param user      需要设置的用户
     * @param confirmed 确认状态 
     */
    public void setEmailConfirmed(TUser user, boolean confirmed) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setEmailConfirmedInternal(user, confirmed);
    }

    /**
     * 设置用户电子邮件确认状态的内部实现。
     *
     * @param user      用户
     * @param confirmed 确认状态
     */
    protected abstract void setEmailConfirmedInternal(TUser user, boolean confirmed);

    /**
     * 设置用户的电子邮件地址。
     *
     * @param user  需要设置的用户
     * @param email 电子邮件地址 
     */
    public void setEmail(TUser user, String email) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setEmailInternal(user, email);
    }

    /**
     * 设置用户电子邮件地址的内部实现。
     *
     * @param user  用户
     * @param email 电子邮件地址
     */
    protected abstract void setEmailInternal(TUser user, String email);

    /**
     * 获取用户的电子邮件地址。
     *
     * @param user 需要获取电子邮件地址的用户
     * @return 用户的电子邮件地址 
     */
    public String getEmail(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getEmailInternal(user);
    }

    /**
     * 获取用户电子邮件地址的内部实现。
     *
     * @param user 用户
     * @return 电子邮件地址
     */
    protected abstract String getEmailInternal(TUser user);

    /**
     * 获取用户的标准化电子邮件地址。
     *
     * @param user 需要获取标准化电子邮件地址的用户
     * @return 用户的标准化电子邮件地址 
     */
    public String getNormalizedEmail(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getNormalizedEmailInternal(user);
    }

    /**
     * 获取用户标准化电子邮件地址的内部实现。
     *
     * @param user 用户
     * @return 标准化电子邮件地址
     */
    protected abstract String getNormalizedEmailInternal(TUser user);

    /**
     * 设置用户的标准化电子邮件地址。
     *
     * @param user            需要设置的用户
     * @param normalizedEmail 标准化电子邮件地址 
     */
    public void setNormalizedEmail(TUser user, String normalizedEmail) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setNormalizedEmailInternal(user, normalizedEmail);
    }

    /**
     * 设置用户标准化电子邮件地址的内部实现。
     *
     * @param user            用户
     * @param normalizedEmail 标准化电子邮件地址
     */
    protected abstract void setNormalizedEmailInternal(TUser user, String normalizedEmail);

    /**
     * 根据标准化电子邮件地址查找用户。
     *
     * @param normalizedEmail 标准化电子邮件地址
     * @return 匹配的用户（如果存在） 
     */
    public abstract Optional<TUser> findByEmail(String normalizedEmail);

    /**
     * 获取用户的锁定结束日期。
     *
     * @param user 需要获取锁定结束日期的用户
     * @return 用户的锁定结束日期 
     */
    public Optional<OffsetDateTime> getLockoutEndDate(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getLockoutEndDateInternal(user);
    }

    /**
     * 获取用户锁定结束日期的内部实现。
     *
     * @param user 用户
     * @return 锁定结束日期
     */
    protected abstract Optional<OffsetDateTime> getLockoutEndDateInternal(TUser user);

    /**
     * 设置用户的锁定结束日期。
     *
     * @param user       需要设置的用户
     * @param lockoutEnd 锁定结束日期 
     */
    public void setLockoutEndDate(TUser user, Optional<OffsetDateTime> lockoutEnd) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setLockoutEndDateInternal(user, lockoutEnd);
    }

    /**
     * 设置用户锁定结束日期的内部实现。
     *
     * @param user       用户
     * @param lockoutEnd 锁定结束日期
     */
    protected abstract void setLockoutEndDateInternal(TUser user, Optional<OffsetDateTime> lockoutEnd);

    /**
     * 增加用户的访问失败计数。
     *
     * @param user 需要增加失败计数的用户
     * @return 增加后的失败计数 
     */
    public int incrementAccessFailedCount(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return incrementAccessFailedCountInternal(user);
    }

    /**
     * 增加用户访问失败计数的内部实现。
     *
     * @param user 用户
     * @return 增加后的失败计数
     */
    protected abstract int incrementAccessFailedCountInternal(TUser user);

    /**
     * 重置用户的访问失败计数。
     *
     * @param user 需要重置失败计数的用户 
     */
    public void resetAccessFailedCount(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        resetAccessFailedCountInternal(user);
    }

    /**
     * 重置用户访问失败计数的内部实现。
     *
     * @param user 用户
     */
    protected abstract void resetAccessFailedCountInternal(TUser user);

    /**
     * 获取用户的访问失败计数。
     *
     * @param user 需要获取失败计数的用户
     * @return 用户的访问失败计数 
     */
    public int getAccessFailedCount(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getAccessFailedCountInternal(user);
    }

    /**
     * 获取用户访问失败计数的内部实现。
     *
     * @param user 用户
     * @return 访问失败计数
     */
    protected abstract int getAccessFailedCountInternal(TUser user);

    /**
     * 获取用户是否可以被锁定。
     *
     * @param user 需要检查的用户
     * @return 如果用户可以被锁定则为true，否则为false 
     */
    public boolean getLockoutEnabled(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getLockoutEnabledInternal(user);
    }

    /**
     * 获取用户是否可以被锁定的内部实现。
     *
     * @param user 用户
     * @return 是否可以被锁定
     */
    protected abstract boolean getLockoutEnabledInternal(TUser user);

    /**
     * 设置用户是否可以被锁定。
     *
     * @param user    需要设置的用户
     * @param enabled 是否可以被锁定 
     */
    public void setLockoutEnabled(TUser user, boolean enabled) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setLockoutEnabledInternal(user, enabled);
    }

    /**
     * 设置用户是否可以被锁定的内部实现。
     *
     * @param user    用户
     * @param enabled 是否可以被锁定
     */
    protected abstract void setLockoutEnabledInternal(TUser user, boolean enabled);

    /**
     * 设置用户的电话号码。
     *
     * @param user        需要设置电话号码的用户
     * @param phoneNumber 电话号码 
     */
    public void setPhoneNumber(TUser user, String phoneNumber) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setPhoneNumberInternal(user, phoneNumber);
    }

    /**
     * 设置用户电话号码的内部实现。
     *
     * @param user        用户
     * @param phoneNumber 电话号码
     */
    protected abstract void setPhoneNumberInternal(TUser user, String phoneNumber);

    /**
     * 获取用户的电话号码。
     *
     * @param user 需要获取电话号码的用户
     * @return 用户的电话号码 
     */
    public String getPhoneNumber(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getPhoneNumberInternal(user);
    }

    /**
     * 获取用户电话号码的内部实现。
     *
     * @param user 用户
     * @return 电话号码
     */
    protected abstract String getPhoneNumberInternal(TUser user);

    /**
     * 获取用户的电话号码是否已确认。
     *
     * @param user 需要检查的用户
     * @return 如果用户电话号码已确认则为true，否则为false 
     */
    public boolean getPhoneNumberConfirmed(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getPhoneNumberConfirmedInternal(user);
    }

    /**
     * 获取用户电话号码是否已确认的内部实现。
     *
     * @param user 用户
     * @return 电话号码确认状态
     */
    protected abstract boolean getPhoneNumberConfirmedInternal(TUser user);

    /**
     * 设置用户的电话号码确认状态。
     *
     * @param user      需要设置的用户
     * @param confirmed 确认状态 
     */
    public void setPhoneNumberConfirmed(TUser user, boolean confirmed) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setPhoneNumberConfirmedInternal(user, confirmed);
    }

    /**
     * 设置用户电话号码确认状态的内部实现。
     *
     * @param user      用户
     * @param confirmed 确认状态
     */
    protected abstract void setPhoneNumberConfirmedInternal(TUser user, boolean confirmed);

    /**
     * 设置用户的安全戳。
     *
     * @param user  需要设置安全戳的用户
     * @param stamp 安全戳 
     */
    public void setSecurityStamp(TUser user, String stamp) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        if (stamp == null) {
            throw new IllegalArgumentException("stamp 不能为 null");
        }
        setSecurityStampInternal(user, stamp);
    }

    /**
     * 设置用户安全戳的内部实现。
     *
     * @param user  用户
     * @param stamp 安全戳
     */
    protected abstract void setSecurityStampInternal(TUser user, String stamp);

    /**
     * 获取用户的安全戳。
     *
     * @param user 需要获取安全戳的用户
     * @return 用户的安全戳 
     */
    public String getSecurityStamp(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getSecurityStampInternal(user);
    }

    /**
     * 获取用户安全戳的内部实现。
     *
     * @param user 用户
     * @return 安全戳
     */
    protected abstract String getSecurityStampInternal(TUser user);

    /**
     * 设置用户的双因素认证是否启用。
     *
     * @param user    需要设置的用户
     * @param enabled 是否启用 
     */
    public void setTwoFactorEnabled(TUser user, boolean enabled) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        setTwoFactorEnabledInternal(user, enabled);
    }

    /**
     * 设置用户双因素认证是否启用的内部实现。
     *
     * @param user    用户
     * @param enabled 是否启用
     */
    protected abstract void setTwoFactorEnabledInternal(TUser user, boolean enabled);

    /**
     * 获取用户的双因素认证是否启用。
     *
     * @param user 需要检查的用户
     * @return 如果用户双因素认证已启用则为true，否则为false 
     */
    public boolean getTwoFactorEnabled(TUser user) {
        throwIfDisposed();
        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        return getTwoFactorEnabledInternal(user);
    }

    /**
     * 获取用户双因素认证是否启用的内部实现。
     *
     * @param user 用户
     * @return 双因素认证是否启用
     */
    protected abstract boolean getTwoFactorEnabledInternal(TUser user);

    /**
     * 查找用户的令牌。
     *
     * @param user          用户
     * @param loginProvider 登录提供程序
     * @param name          令牌名称
     * @return 用户令牌（如果存在） 
     */
    protected abstract Optional<TUserToken> findToken(TUser user, String loginProvider, String name);

    /**
     * 添加用户令牌。
     *
     * @param token 要添加的令牌 
     */
    protected abstract void addUserToken(TUserToken token);

    /**
     * 移除用户令牌。
     *
     * @param token 要移除的令牌 
     */
    protected abstract void removeUserToken(TUserToken token);

    /**
     * 设置用户的令牌值。
     *
     * @param user          用户
     * @param loginProvider 登录提供程序
     * @param name          令牌名称
     * @param value         令牌值 
     */
    public void setToken(TUser user, String loginProvider, String name, String value) {
        throwIfDisposed();

        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }

        Optional<TUserToken> tokenOpt = findToken(user, loginProvider, name);
        if (!tokenOpt.isPresent()) {
            addUserToken(createUserToken(user, loginProvider, name, value));
        } else {
            TUserToken token = tokenOpt.get();
            setTokenValue(token, value);
            updateUserToken(token);
        }
    }

    /**
     * 设置令牌值。
     *
     * @param token 令牌
     * @param value 值
     */
    protected abstract void setTokenValue(TUserToken token, String value);

    /**
     * 更新用户令牌。
     *
     * @param token 要更新的令牌 
     */
    protected abstract void updateUserToken(TUserToken token);

    /**
     * 移除用户的令牌。
     *
     * @param user          用户
     * @param loginProvider 登录提供程序
     * @param name          令牌名称 
     */
    public void removeToken(TUser user, String loginProvider, String name) {
        throwIfDisposed();

        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }

        Optional<TUserToken> entry = findToken(user, loginProvider, name);
        if (entry.isPresent()) {
            removeUserToken(entry.get());
        }
    }

    /**
     * 获取用户的令牌值。
     *
     * @param user          用户
     * @param loginProvider 登录提供程序
     * @param name          令牌名称
     * @return 令牌值 
     */
    public Optional<String> getToken(TUser user, String loginProvider, String name) {
        throwIfDisposed();

        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }

        Optional<TUserToken> entry = findToken(user, loginProvider, name);
        return entry.map(this::getTokenValue);
    }

    /**
     * 获取令牌值。
     *
     * @param token 令牌
     * @return 值
     */
    protected abstract String getTokenValue(TUserToken token);

    /**
     * 设置用户的认证器密钥。
     *
     * @param user 用户
     * @param key  密钥 
     */
    public void setAuthenticatorKey(TUser user, String key) {
        setToken(user, INTERNAL_LOGIN_PROVIDER, AUTHENTICATOR_KEY_TOKEN_NAME, key);
    }

    /**
     * 获取用户的认证器密钥。
     *
     * @param user 用户
     * @return 认证器密钥 
     */
    public Optional<String> getAuthenticatorKey(TUser user) {
        return getToken(user, INTERNAL_LOGIN_PROVIDER, AUTHENTICATOR_KEY_TOKEN_NAME);
    }

    /**
     * 计算用户的有效恢复码数量。
     *
     * @param user 用户
     * @return 有效恢复码数量 
     */
    public int countCodes(TUser user) {
        throwIfDisposed();

        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }

        Optional<String> mergedCodesOpt = getToken(user, INTERNAL_LOGIN_PROVIDER, RECOVERY_CODE_TOKEN_NAME);
        String mergedCodes = mergedCodesOpt.orElse("");

        if (!mergedCodes.isEmpty()) {
            return mergedCodes.split(";").length;
        }
        return 0;
    }

    /**
     * 替换用户的恢复码。
     *
     * @param user          用户
     * @param recoveryCodes 新的恢复码列表 
     */
    public void replaceCodes(TUser user, List<String> recoveryCodes) {
        String mergedCodes = String.join(";", recoveryCodes);
        setToken(user, INTERNAL_LOGIN_PROVIDER, RECOVERY_CODE_TOKEN_NAME, mergedCodes);
    }

    /**
     * 使用恢复码并验证其有效性。
     *
     * @param user 用户
     * @param code 恢复码
     * @return 如果恢复码有效则为true，否则为false 
     */
    public boolean redeemCode(TUser user, String code) {
        throwIfDisposed();

        if (user == null) {
            throw new IllegalArgumentException("user 不能为 null");
        }
        if (code == null) {
            throw new IllegalArgumentException("code 不能为 null");
        }

        Optional<String> mergedCodesOpt = getToken(user, INTERNAL_LOGIN_PROVIDER, RECOVERY_CODE_TOKEN_NAME);
        String mergedCodes = mergedCodesOpt.orElse("");

        String[] splitCodes = mergedCodes.split(";");
        List<String> codeList = new ArrayList<>();
        boolean found = false;

        for (String splitCode : splitCodes) {
            if (splitCode.equals(code)) {
                found = true;
            } else {
                codeList.add(splitCode);
            }
        }

        if (found) {
            replaceCodes(user, codeList);
            return true;
        }
        return false;
    }
}